﻿package com.sff.webservice {
	
	import com.sff.webservice.data.WSResponder;
	import com.sff.webservice.events.OperationEvent;
	import com.sff.webservice.utils.WSUtils;
	
	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.net.URLRequestHeader;
	import flash.net.URLRequestMethod;
	
	public class Operation extends EventDispatcher {
		
		private var ws			:WebService;
		private var arrItem		:Array;
		private var curItem		:OperationLoaderItem;
		private var isLoading	:Boolean = false;
		
		public function Operation(pWS:WebService) {
			ws = pWS;
			
			arrItem = new Array();
		}

		private function createURLRequest(item:OperationLoaderItem): URLRequest {			
			var urlRequest:URLRequest = new URLRequest(ws.wsdlURL);
			
			urlRequest.contentType = "text/xml; charset=utf-8";
			urlRequest.method = URLRequestMethod.POST;
			urlRequest.requestHeaders.push(new URLRequestHeader("Content-Type", "text/xml; charset=utf-8"));
			urlRequest.requestHeaders.push(new URLRequestHeader("SOAPAction", createSoapAction(ws.operations.targetNameSpace) + item.methodName));
			urlRequest.data = WSUtils.createDataVariables(item.methodName, ws.operations, ws.operations.targetNameSpace, item.arguments);

			return urlRequest;
		}
		
		private function createSoapAction(soapAction:String):String {
			var isSlash:Boolean = (soapAction.lastIndexOf("/") == -1 || soapAction.lastIndexOf("/") < soapAction.length - 2);
			return soapAction + (isSlash ? "/" : "");
		}
		
		private function next():void {
			
			if (curItem) {
				return
			} else {
				
				if (arrItem.length) {				
					curItem = arrItem.shift();					
					curItem.addEventListener(Event.COMPLETE, onOperationCompleteHandler);
					curItem.addEventListener(IOErrorEvent.IO_ERROR, onOperationErrorHandler);			
					curItem.load(createURLRequest(curItem));
				}
			}
		}

		private function onOperationCompleteHandler(evt:Event):void {	
			try {
				var response:WSResponder = new WSResponder(new XML(evt.currentTarget.data), ws.operations);
				dispatchEvent(new OperationEvent(OperationEvent.RESULT, curItem.methodName, response.data));
			} 
			
			catch (err:Error) {
				dispatchEvent(new OperationEvent(OperationEvent.FAILED, curItem.methodName, err));
			}
			
			curItem.removeEventListener(Event.COMPLETE, onOperationCompleteHandler);
			curItem.removeEventListener(IOErrorEvent.IO_ERROR, onOperationErrorHandler);			
			curItem = null;
			
			next();
		}

		private function onOperationErrorHandler(err:IOErrorEvent):void {
			dispatchEvent(new OperationEvent(OperationEvent.FAILED, curItem.methodName, err));
			
			curItem.removeEventListener(Event.COMPLETE, onOperationCompleteHandler);
			curItem.removeEventListener(IOErrorEvent.IO_ERROR, onOperationErrorHandler);			
			curItem = null;
			
			next();
		}

		public function call(methodName:String, ... arguments): void {
			
			if (ws.operations.methodExists(methodName)) {				
				var item:OperationLoaderItem = new OperationLoaderItem(methodName, arguments);
				arrItem.push(item);				
			} else {				
				dispatchEvent(new OperationEvent(OperationEvent.FAILED, methodName));
			}
			
			next();
		}
		
		public function cancel(methodName:String):void {			
			if (ws.operations.methodExists(methodName) == false) {
				return;
			}
			
			if (curItem && curItem.methodName == methodName) {
				curItem.removeEventListener(Event.COMPLETE, onOperationCompleteHandler);
				curItem.removeEventListener(IOErrorEvent.IO_ERROR, onOperationErrorHandler);			
				curItem = null;				
			} else {
				
				var item:OperationLoaderItem;
				for (var i:int = 0; i < arrItem.length; i ++) {
					item = arrItem[i];
				
					if (item.methodName == methodName) {
						break;
					}
					item = null;
				}
			
				arrItem.splice(arrItem.indexOf(item), 1)
			}
			
			//load next
			next();
		}
	}
}

import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;

internal class OperationLoaderItem extends URLLoader {
	
	private var _methodName:String;
	private var _arguments:Array;
	
	public function OperationLoaderItem(pMethodName:String, pArguments:Array) {
		super();
		
		_methodName = pMethodName;
		_arguments = pArguments;
		
		dataFormat = URLLoaderDataFormat.TEXT;
	}
	
	public function get methodName():String {
		return _methodName;
	}
	
	public function get arguments():Array {
		return _arguments;
	}		
}	